from managers import *
from utils import sortable
import settings
import utils

from django.db import models
from django.template import loader, Context, TemplateDoesNotExist
from django.utils.translation import ugettext_lazy as _
from django.contrib.sites.models import Site
from django.utils.encoding import smart_unicode

import md5
import os
import datetime

MEDIA_CHOICES = (
    ( 'all', _('all') ),
    ( 'screen', _('screen') ),
    ( 'print', _('print') ),
    ( 'projection', _('projection') ),
    )

preprocessors = {
    'sass': ['farstyle.filters.sass.SASSFilter'],
    'less': ['farstyle.filters.less.LESSFilter'],
}

def preprocess(f_path, type, processors):
    src_mtime = os.path.getmtime(src_path)
    try:
        dest_mtime = os.path.getmtime(dest_path)
    except OSError:
        pass
    else:
        if dest_mtime >= src_mtime:
            return

    f = open(src_path)
    str = f.read()
    f.close()
    filter = getattr(utils, 'filter_%s' % type)
    str = filter(str, processors)
    f = open(dest_path, 'wb')
    f.write(str)
    f.close()

class Asset(models.Model, sortable.Model):
    """
    Abstract superclass describing an asset file.
    """
    path = models.CharField(_('path'), max_length=100,
        help_text=_('Relative to MEDIA_ROOT'))
    condition = models.CharField(_('condition'), max_length=100, blank=True,
        help_text=_("""
            Enter a python expression which should evaluate to a true value when the
            asset is to be used.
            """))
    compress = models.BooleanField(_('compress'), default=True)
    combine = models.BooleanField(_('combine'), default=True)
    sites = models.ManyToManyField(Site, verbose_name=_('sites'))
    order = sortable.OrderingField(verbose_name=_('order'))

    class Meta:
        abstract = True
        ordering = ('order',)

    def guard(self, context, params):
        if not self.condition:
            return not params
        locals = {'params': params,
                  'context': context,
                  'DEBUG': settings.DEBUG}
        # Naked except... who knows what users might put in there
        try:
            return eval(self.condition, {}, locals)
        except:
            return False

    def ident(self, context):
        if self.is_template:
            rendered = self.render()
            m = md5.new()
            m.update(unicode(rendered))
            return m.hexdigest()

        if not hasattr(self, '_fullpath'):
            return ''
        mtime = os.path.getmtime(self._fullpath)
        return str(mtime)

    def hash(self, context):
        m = md5.new()
        if self.is_template:
            rendered = self.render()
            m.update(rendered.encode('utf-8'))
        else:
            mtime = os.path.getmtime(self._fullpath)
            m.update(unicode(mtime))
        return m.hexdigest()

    def _find_file(self):
        try:
            t = loader.get_template(self.path)
            self._template = t
            self._is_template = True
            self._url = '/fs/%s/%s/' % (self.type, self.path)

        except TemplateDoesNotExist:
            self._is_template = False
            fpath = os.path.join(settings.MEDIA_ROOT, self.path)
            if self.preprocessors:
                self._url = '/fs/%s/%s/' % (self.type, self.path)
            else:
                self._url = '%s%s' % (settings.MEDIA_URL, self.path)

            if os.path.exists(fpath):
                self._fullpath = fpath
            else:
                if settings.DEBUG:
                    raise IOError, "File %s does not exist (searched in template directories and in %s)" % \
                                   (self.path, settings.MEDIA_ROOT)

    @property
    def norm_path(self):
        return os.path.splitext(self.path)[0]

    @property
    def template(self):
        if not hasattr(self, '_template'):
            self._find_file()
        return self._template

    @property
    def is_template(self):
        if not hasattr(self, '_is_template'):
            self._find_file()
        return self._is_template

    @property
    def preprocessors(self):
        ext = os.path.splitext(self.path)[1][1:]
        return preprocessors.get(ext, [])

    def get_absolute_url(self):
        if not hasattr(self, '_url'):
            self._find_file()
        return self._url

    def render(self):
        if hasattr(self, '_rendered'):
            return self._rendered

        if self.is_template:
            str = self.render_from_template()
        else:
            str = self.render_from_file()

        filter = getattr(utils, 'filter_%s' % self.type)
        str = filter(str, self.preprocessors)
        self._rendered = smart_unicode(str)
        return self._rendered

    def render_from_file(self):
        if hasattr(self, '_fullpath'):
            f = open(self._fullpath)
            str = f.read()
            f.close()
        else:
            str = u''
        return str

    def reorder(self):
        objs = list(self.__class__.objects.all())
        s = []
        if objs[0] != self:
            s.append('<a href="%s/move/up/">Up</a>' % self.id)
        if objs[-1] != self:
            s.append('<a href="%s/move/down/">Down</a>' % self.id)
        if len(s) > 1:
            s.insert(1, '/')
        return '&nbsp;'.join(s)
    reorder.short_description = _('order')
    reorder.allow_tags = True
    reorder.admin_order_field = 'order'

class CSS(Asset, sortable.Model):
    """
    A css asset.
    """
    media = models.CharField(_('media'), choices=MEDIA_CHOICES, max_length=12, default='all')

    objects = CSSManager()

    type = 'css'

    class Meta(Asset.Meta):
        verbose_name = 'css file'
        verbose_name_plural = 'css files'

    def __unicode__(self):
        return u'%s [%s]' % (self.path, self.get_media_display())

    def render_from_template(self):
        extra = {
            'media_url': settings.MEDIA_URL,
            'admin_media_url': settings.ADMIN_MEDIA_PREFIX,
            }
        module = getattr(settings, 'STYLES_MODULE', None)
        if module is not None:
            try:
                mod = __import__(module, {}, {}, [''])
            except ImportError, e:
                raise EnvironmentError, "Could not import styles module '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (module, e)
            for setting in dir(mod):
                setting_value = getattr(mod, setting)
                extra[setting] = setting_value
        c = Context(extra)
        str = self.template.render(c)
        return str

class JS(Asset):
    """
    A javascript asset.
    """
    objects = JSManager()

    type = 'js'

    _move_up_or_down = sortable.Model._move_up_or_down
    
    class Meta(Asset.Meta):
        verbose_name = 'javascript file'
        verbose_name_plural = 'javascript files'

    def __unicode__(self):
        return self.path

    def render_from_template(self, context):
        extra = {
            'media_url': settings.MEDIA_URL,
            'admin_media_url': settings.ADMIN_MEDIA_PREFIX,
            }
        c = Context(extra)
        str = self.template.render(c)
        filter_js(str, self.preprocessors)
        return str
